package com.vanke.agent.common;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.vanke.agent.ADataSource;
import com.vanke.agent.bean.Mapping;

public class Cache {
	/*
	 * 缓存映射，共分三级；
	 * 第一级：关键字段与缓存的映射，即手机、身份证或明源GUID对应的映射缓存map
	 * 第二级：缓存map，存储关键字段后缀的对应的映射列表，即手机、身份证或明源GUID的后两位
	 * 第三级：实际的映射存储，即手机、身份证或明源GUID对应的数据中心GUID映射
	 */
	protected static Map<String, Map<String, Map<String, Long>>> cacheMap = new HashMap<String, Map<String, Map<String, Long>>>();

	/*
	 * 映射列表，即一个客户GUID对应的手机列表、身份证列表或明源GUID列表
	 */
	protected static Map<Long, Map<String, Set<String>>> cstKeyMap = new HashMap<Long, Map<String, Set<String>>>();

	protected static final String MOBILE = "mobile";
	protected static final String CARDID = "cardid";
	protected static final String ORIGUID = "origuid";

	protected static Map<String, String> prefixMap = new HashMap<String, String>();

	static {
		prefixMap.put(MOBILE, "cst_cstmobilemapping_");
		prefixMap.put(CARDID, "cst_cstcardidmapping_");
		prefixMap.put(ORIGUID, "cst_cstguidmapping_");

		cacheMap.put(MOBILE, new HashMap<String, Map<String, Long>>());
		cacheMap.put(CARDID, new HashMap<String, Map<String, Long>>());
		cacheMap.put(ORIGUID, new HashMap<String, Map<String, Long>>());
	}

	/*
	 * 加载手机映射
	 */
	public static int loadMobileMapping(Connection con) {
		return doLoadMapping(con, MOBILE);
	}

	/*
	 * 加载身份证映射
	 */
	public static int loadCardIdMapping(Connection con) {
		return doLoadMapping(con, CARDID);
	}

	/*
	 * 加载明源GUID映射
	 */
	public static int loadGUIDMapping(Connection con) {
		return doLoadMapping(con, ORIGUID);
	}

	/*
	 * 缓存手机映射
	 */
	public static long cacheMobile(String mobile, long guid) {
		return doCache(mobile, guid, MOBILE);
	}

	/*
	 * 缓存身份证映射
	 */
	public static long cacheCardId(String cardid, long guid) {
		return doCache(cardid, guid, CARDID);
	}

	/*
	 * 缓存GUID映射
	 */
	public static long cacheOriGuid(String origuid, long guid) {
		return doCache(origuid, guid, ORIGUID);
	}

	/*
	 * 根据手机取数据中心GUID
	 */
	public static long getGuidByMobile(String mobile) {
		return doGetGuid(mobile, MOBILE);
	}

	/*
	 * 根据身份证取数据中心GUID
	 */
	public static long getGuidByCardId(String cardid) {
		return doGetGuid(cardid, CARDID);
	}

	/*
	 * 根据明源GUID取数据中心GUID
	 */
	public static long getGuidByOriGuid(String origuid) {
		return doGetGuid(origuid, ORIGUID);
	}

	/*
	 * 根据GUID取手机列表
	 */
	public static Set<String> getMobiles(long guid) {
		return doGetKeys(guid, MOBILE);
	}

	/*
	 * 根据GUID取身份证列表
	 */
	public static Set<String> getCardIds(long guid) {
		return doGetKeys(guid, CARDID);
	}

	/*
	 * 根据GUID取明源GUID列表
	 */
	public static Set<String> getOriGuids(long guid) {
		return doGetKeys(guid, ORIGUID);
	}

	public static void releaseCache() {
		cacheMap.get(MOBILE).clear();
		cacheMap.get(CARDID).clear();
		cacheMap.get(ORIGUID).clear();
		cstKeyMap.clear();
	}

	public static void releaseMobileCache() {
		cacheMap.get(MOBILE).clear();
		doRelease(MOBILE);
	}

	public static void releaseCardIdCache() {
		cacheMap.get(CARDID).clear();
		doRelease(CARDID);
	}

	public static void releaseOriGuidCache() {
		cacheMap.get(ORIGUID).clear();
		doRelease(ORIGUID);
	}

	private static void doRelease(String key) {
		Iterator<Entry<Long, Map<String, Set<String>>>> iter;
		iter = cstKeyMap.entrySet().iterator();
		while (iter.hasNext()) {
			Entry<Long, Map<String, Set<String>>> entry = iter.next();
			Map<String, Set<String>> map = entry.getValue();
			Set<String> set = map.get(key);
			if (set != null) {
				set.clear();
				map.remove(key);
			}
		}
	}

	private static long doCache(String key, long guid, String mapField) {
		if (key == null || key.length() < 2) {
			return 0;
		}
		Map<String, Map<String, Long>> mobileMap = cacheMap.get(mapField);
		String suffix = key.substring(key.length() - 2);

		long oldGuid = doGetGuid(key, mapField); // 取旧的映射
		if (oldGuid != 0 && oldGuid != guid) {
			// 如果映射已存在，且与当前映射不等，则从旧的映射列表中移除
			Set<String> mobiles = doGetKeys(oldGuid, mapField);
			mobiles.remove(key);
		}

		// 根据后缀取映射表
		Map<String, Long> map = mobileMap.get(suffix);
		if (map == null) {
			map = new HashMap<String, Long>();
			mobileMap.put(suffix, map);
		}
		Set<String> mobiles = doGetKeys(guid, mapField);
		mobiles.add(key);
		Long l = map.put(key, guid);
		return l == null ? 0 : l;
	} //~doCache(String key, long guid, String mapField)

	private static long doGetGuid(String key, String mapField) {
		try {
			Map<String, Map<String, Long>> keyMap;
			keyMap = cacheMap.get(mapField);
			String suffix = key.substring(key.length() - 2);
			Map<String, Long> map = keyMap.get(suffix);
			Long guid = map == null ? new Long(0) : map.get(key);
			return guid == null ? 0 : guid;
		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		}
	}

	private static Set<String> doGetKeys(long guid, String key) {
		Map<String, Set<String>> map = cstKeyMap.get(guid);
		if (map == null) {
			map = new HashMap<String, Set<String>>();
			cstKeyMap.put(guid, map);
		}
		Set<String> set = map.get(key);
		if (set == null) {
			set = new HashSet<String>();
			map.put(key, set);
		}
		return set;
	}

	/*
	 * 加载映射列表
	 */
	private static int doLoadMapping(Connection con, String keyField) {
		Statement stmt = null;
		int count = 0;
		try {
			Map<String, Map<String, Long>> uidMap = cacheMap.get(keyField);
			// 加载所有映射表
			List<String> tables = loadTables(con, prefixMap.get(keyField));
			stmt = con.createStatement();
			for (String table : tables) { // 遍历取表
				String sql = "select " + keyField + ",CstGUID from " + table;
				ResultSet rs = stmt.executeQuery(sql);
				while (rs.next()) {
					count++;

					// 键值，即手机、身份证或明源GUID
					String key = rs.getString(1).trim();

					// 数据中心GUID
					long cstguid = rs.getLong(2);

					String suffix = key.substring(key.length() - 2); // 取两位后缀

					// 缓存键值对数据中心GUID的映射
					Map<String, Long> map = uidMap.get(suffix);
					if (map == null) {
						map = new HashMap<String, Long>();
						uidMap.put(suffix, map);
					}
					map.put(key, cstguid);

					// 缓存数据中心GUID对键值列表的映射
					Map<String, Set<String>> mapList = cstKeyMap.get(cstguid);
					if (mapList == null) {
						mapList = new HashMap<String, Set<String>>();
						cstKeyMap.put(cstguid, mapList);
					}
					Set<String> set = mapList.get(keyField);
					if (set == null) {
						set = new HashSet<String>();
						mapList.put(keyField, set);
					}
					set.add(key);
				} // end while
				ADataSource.close(rs);
			} // end for (String table : tables)
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			ADataSource.close(stmt);
		}
		return count;
	} //~doLoadMapping

	public static List<String> loadTables(Connection con, String prefix) {
		Statement stmt = null;
		ResultSet rs = null;
		List<String> tables = new ArrayList<String>(10);
		try {
			stmt = con.createStatement();
			rs = stmt.executeQuery("show tables");
			while (rs.next()) {
				String t = rs.getString(1);
				if (t.toLowerCase().startsWith(prefix)) {
					tables.add(t);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			ADataSource.close(rs, stmt, null);
		}
		return tables;
	} //~


	/**
	 * 加载客户的所有映射,包含手机、身份证、明源GUID；一级映射为表，即客户所在的表；第二级映射<br>
	 * 为客户ID与映射列表
	 * @see Mapping
	 */
	public static Map<String, Map<Long, Mapping>> loadCache(Connection bizCon,
			Set<Long> csts) {
		Map<String, Map<Long, Mapping>> mappings = new HashMap<String, Map<Long, Mapping>>();
		for (Long guid : csts) {
			if (guid == null) {
				continue;
			}

			String tbl = CstUtil.retrieveCustomerTable(guid);
			Map<Long, Mapping> map = mappings.get(tbl);
			if (map == null) {
				map = new HashMap<Long, Mapping>();
				mappings.put(tbl, map);
			}
			map.put(guid, new Mapping());
		} //end for
		try {
			loadCstMobileMapping(bizCon, csts, mappings);
			loadCardIdMapping(bizCon, csts, mappings);
			loadOriGUIDMapping(bizCon, csts, mappings);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		return mappings;
	} //~loadCache(Set<Long> csts)

	public static int loadCstMobileMapping(Connection bizCon, Set<Long> csts,
			Map<String, Map<Long, Mapping>> mappings) throws SQLException {
		if (csts.size() == 0) {
			return 0;
		}
		Statement stmt = null;
		ResultSet rs = null;
		int count = 0;
		try {
			List<String> tables = Cache.loadTables(bizCon,
					"cst_cstmobilemapping_");
			String ids = csts.toString().replace('[', '(').replace(']', ')');
			for (String table : tables) {
				stmt = bizCon.createStatement();
				String sql = "select mobile,CstGUID from " + table
						+ " where CstGUID in" + ids;
				rs = stmt.executeQuery(sql);
				while (rs.next()) {
					count++;
					String mobile = rs.getString(1);
					long guid = rs.getLong(2);
					String tbl = CstUtil.retrieveCustomerTable(guid);
					Map<Long, Mapping> map = mappings.get(tbl);
					if (map == null) {
						continue;
					}
					Mapping m = map.get(guid);
					if (m == null) {
						continue;
					}
					m.getMobiles().add(mobile);
				} // end while
			} // end for
		} catch (SQLException e) {
			throw e;
		} finally {
			ADataSource.close(rs, stmt, null);
		}
		return count;
	} //~loadCstMobileMapping

	public static int loadCardIdMapping(Connection bizCon, Set<Long> csts,
			Map<String, Map<Long, Mapping>> mappings) throws SQLException {
		if (csts.size() == 0) {
			return 0;
		}
		Statement stmt = null;
		ResultSet rs = null;
		int count = 0;
		try {
			List<String> tables = Cache.loadTables(bizCon,
					"cst_cstcardidmapping_");
			String ids = csts.toString().replace('[', '(').replace(']', ')');
			for (String table : tables) {
				stmt = bizCon.createStatement();
				String sql = "select cardId,CstGUID from " + table
						+ " where CstGUID in" + ids;
				rs = stmt.executeQuery(sql);
				while (rs.next()) {
					count++;
					String cardId = rs.getString(1);
					long guid = rs.getLong(2);
					String tbl = CstUtil.retrieveCustomerTable(guid);
					Map<Long, Mapping> map = mappings.get(tbl);
					if (map == null) {
						continue;
					}
					Mapping m = map.get(guid);
					if (m == null) {
						continue;
					}
					m.getCardIds().add(cardId);
				} // end while
			} // end for
		} catch (SQLException e) {
			throw e;
		} finally {
			ADataSource.close(rs, stmt, null);
		}
		return count;
	} //~loadCardIdMapping

	public static int loadOriGUIDMapping(Connection bizCon, Set<Long> csts,
			Map<String, Map<Long, Mapping>> mappings) throws SQLException {
		if (csts.size() == 0) {
			return 0;
		}
		Statement stmt = null;
		ResultSet rs = null;
		int count = 0;
		try {
			List<String> tables = Cache.loadTables(bizCon,
					"cst_cstguidmapping_");
			String ids = csts.toString().replace('[', '(').replace(']', ')');
			for (String table : tables) {
				stmt = bizCon.createStatement();
				String sql = "select OriGUID,CstGUID from " + table
						+ " where CstGUID in" + ids;
				rs = stmt.executeQuery(sql);
				while (rs.next()) {
					count++;
					String oriGuid = rs.getString(1);
					long guid = rs.getLong(2);
					String tbl = CstUtil.retrieveCustomerTable(guid);
					Map<Long, Mapping> map = mappings.get(tbl);
					if (map == null) {
						continue;
					}
					Mapping m = map.get(guid);
					if (m == null) {
						continue;
					}
					m.getOriGuids().add(oriGuid);
				} // end while
			} // end for
		} catch (SQLException e) {
			throw e;
		} finally {
			ADataSource.close(rs, stmt, null);
		}
		return count;
	} //~loadCardIdMapping
	
	public static void main(String[] args) {
		System.out.println(String.format("%s:%s,%s:%s", 123, true, 456, false));
		//		testMobileCache();
		//		System.out.println("================================================");
		//		testCardIdCache();
		//		System.out.println("================================================");
		//		testOriGuidCache();
		//		System.out.println("================================================");
		//		System.out.println("================================================");
		//
		//		print();
	} //~main

	static void print() {
		// 请除缓存，全取不到
		System.out.println("13414452756:" + getGuidByMobile("13414452756"));
		System.out.println("13414452757:" + getGuidByMobile("13414452757"));
		System.out.println("123:" + getMobiles(123));
		System.out.println("456:" + getMobiles(456));
		System.out.println("------------------------------------------------");
		System.out.println("3411268:" + getGuidByCardId("3411268"));
		System.out.println("3411269:" + getGuidByCardId("3411269"));
		System.out.println("123:" + getCardIds(123));
		System.out.println("456:" + getCardIds(456));
		System.out.println("------------------------------------------------");
		System.out.println("FA-XAB:" + getGuidByOriGuid("FA-XAB"));
		System.out.println("FA-XAC:" + getGuidByOriGuid("FA-XAC"));
		System.out.println("123:" + getOriGuids(123));
		System.out.println("456:" + getOriGuids(456));
		System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++");
		releaseCache();
		System.out.println("13414452756:" + getGuidByMobile("13414452756"));
		System.out.println("13414452757:" + getGuidByMobile("13414452757"));
		System.out.println("123:" + getMobiles(123));
		System.out.println("456:" + getMobiles(456));
		System.out.println("------------------------------------------------");
		System.out.println("3411268:" + getGuidByCardId("3411268"));
		System.out.println("3411269:" + getGuidByCardId("3411269"));
		System.out.println("123:" + getCardIds(123));
		System.out.println("456:" + getCardIds(456));
		System.out.println("------------------------------------------------");
		System.out.println("FA-XAB:" + getGuidByOriGuid("FA-XAB"));
		System.out.println("FA-XAC:" + getGuidByOriGuid("FA-XAC"));
		System.out.println("123:" + getOriGuids(123));
		System.out.println("456:" + getOriGuids(456));
	} //~

	static void testMobileCache() {
		System.out.println("开始是空缓存，都取不到才对");
		System.out.println("13414452756:" + getGuidByMobile("13414452756"));
		System.out.println("123:" + getMobiles(123));
		System.out.println("------------------------------------------------");
		cacheMobile("13414452756", 123);
		System.out.println("缓存之后，全能取到才对");
		System.out.println("13414452756:" + getGuidByMobile("13414452756"));
		System.out.println("123:" + getMobiles(123));
		System.out.println("------------------------------------------------");

		System.out.println("再缓存一个手机，按手机都能取到，按GUID取到俩才对");
		cacheMobile("13414452757", 123);
		System.out.println("13414452756:" + getGuidByMobile("13414452756"));
		System.out.println("13414452757:" + getGuidByMobile("13414452757"));
		System.out.println("123:" + getMobiles(123));
		System.out.println("------------------------------------------------");

		System.out.println("缓存同样手机到其它GUID，那么123只剩余一个手机才对");
		cacheMobile("13414452757", 456);
		System.out.println("13414452756:" + getGuidByMobile("13414452756"));
		System.out.println("13414452757:" + getGuidByMobile("13414452757"));
		System.out.println("123:" + getMobiles(123));
		System.out.println("456:" + getMobiles(456));
	}

	static void testCardIdCache() {
		System.out.println("开始是空缓存，都取不到才对");
		System.out.println("3411268:" + getGuidByCardId("3411268"));
		System.out.println("123:" + getCardIds(123));
		System.out.println("------------------------------------------------");
		cacheCardId("3411268", 123);
		System.out.println("缓存之后，全能取到才对");
		System.out.println("3411268:" + getGuidByCardId("3411268"));
		System.out.println("123:" + getCardIds(123));
		System.out.println("------------------------------------------------");

		System.out.println("再缓存一个身份证，按身份证都能取到，按GUID取到俩才对");
		cacheCardId("3411269", 123);
		System.out.println("3411268:" + getGuidByCardId("3411268"));
		System.out.println("3411269:" + getGuidByCardId("3411269"));
		System.out.println("123:" + getCardIds(123));
		System.out.println("------------------------------------------------");

		System.out.println("缓存同样身份证到其它GUID，那么123只剩余一个身份证才对");
		cacheCardId("3411269", 456);
		System.out.println("3411268:" + getGuidByCardId("3411268"));
		System.out.println("3411269:" + getGuidByCardId("3411269"));
		System.out.println("123:" + getCardIds(123));
		System.out.println("456:" + getCardIds(456));
	}

	static void testOriGuidCache() {
		System.out.println("开始是空缓存，都取不到才对");
		System.out.println("FA-XAB:" + getGuidByOriGuid("FA-XAB"));
		System.out.println("123:" + getOriGuids(123));
		System.out.println("------------------------------------------------");
		cacheOriGuid("3411268", 123);
		System.out.println("缓存之后，全能取到才对");
		System.out.println("FA-XAB:" + getGuidByOriGuid("FA-XAB"));
		System.out.println("123:" + getOriGuids(123));
		System.out.println("------------------------------------------------");

		System.out.println("再缓存一个明源GUID，按明源GUID都能取到，按GUID取到俩才对");
		cacheOriGuid("FA-XAC", 123);
		System.out.println("FA-XAB:" + getGuidByOriGuid("FA-XAB"));
		System.out.println("FA-XAC:" + getGuidByOriGuid("FA-XAC"));
		System.out.println("123:" + getOriGuids(123));
		System.out.println("------------------------------------------------");

		System.out.println("缓存同样明源GUID到其它GUID，那么123只剩余一个明源GUID才对");
		cacheOriGuid("FA-XAC", 456);
		System.out.println("FA-XAB:" + getGuidByOriGuid("FA-XAB"));
		System.out.println("FA-XAC:" + getGuidByOriGuid("FA-XAC"));
		System.out.println("123:" + getOriGuids(123));
		System.out.println("456:" + getOriGuids(456));
	}
} ///~Cache
